package com.lmy.timer;


import java.util.Date;
import java.util.Timer;

/**
 * JDK定时器timer使用及源码分析
 * 存在问题：单线程启动任务 task.run() 而不是 new Thread(task).start()，如任务超时会导致任务阻塞
 * 解决方案：在定时任务对象的run()中指定线程池执行定时任务逻辑
 */
public class TimerTaskTest {

    public static void main(String[] args) {
        testTimerTask();
    }

    public static void testTimerTask() {
        //1.创建定时器Timer，这一步就已经启动了定时任务
        /*
            =============================================
            Timer.java
            //真正的定时任务对象放在 TaskQueue 任务队列中，其数据结构就是小顶堆
            private final TaskQueue queue = new TaskQueue();

            //TimeThread 是用来执行定时任务的线程
            private final TimerThread thread = new TimerThread(queue);

            //这里通过无参构造器创建Timer定时器时，会调用 thread.start()，开启 TimerThread 的多线程任务
            public Timer() {
                this("Timer-" + serialNumber());
            }

            public Timer(String name) {
                thread.setName(name);
                thread.start();
            }

            ==================================
            TimerThread.java

            //TimerThread 的run()
            public void run() {
                try {
                    mainLoop();//真正启动定时任务的run()
                } finally { //清理队列
                    // Someone killed this Thread, behave as if Timer cancelled
                    synchronized(queue) {
                        newTasksMayBeScheduled = false;
                        queue.clear();  // Eliminate obsolete references
                    }
                }
            }

            //mainLoop()是真正执行定时任务对象中的run()的方法
            private void mainLoop() {
                while (true) {
                    try {
                        TimerTask task; //这里声明了一个定时任务对象
                        boolean taskFired;
                        synchronized(queue) {
                            // Wait for queue to become non-empty
                            while (queue.isEmpty() && newTasksMayBeScheduled)   //这里队列为空时，会阻塞
                                queue.wait();
                            if (queue.isEmpty())
                                break; // Queue is empty and will forever remain; die

                            // Queue nonempty; look at first evt and do the right thing
                            long currentTime, executionTime;
                            task = queue.getMin();  //这里从小顶堆的堆顶取出一个定时任务对象
                            synchronized(task.lock) {
                                if (task.state == TimerTask.CANCELLED) {
                                    queue.removeMin();  //取出堆顶的元素后，再次堆化(最后一个节点上浮，然后按规则下沉)
                                    continue;  // No action required, poll queue again
                                }
                                currentTime = System.currentTimeMillis();   //获取系统当前时间
                                executionTime = task.nextExecutionTime; //获取task的下一次的执行时间(预设)
                                if (taskFired = (executionTime<=currentTime)) {
                                    if (task.period == 0) { // Non-repeating, remove
                                        queue.removeMin();
                                        task.state = TimerTask.EXECUTED;
                                    } else { // Repeating task, reschedule
                                        queue.rescheduleMin(    //将新任务重新入队
                                          task.period<0 ? currentTime   - task.period
                                                        : executionTime + task.period);
                                    }
                                }
                            }
                            if (!taskFired) // Task hasn't yet fired; wait
                                queue.wait(executionTime - currentTime);
                        }
                        if (taskFired)  // Task fired; run it, holding no locks
                            task.run(); //启动定时任务，注意只是单线程启动，并没有调用start()
                    } catch(InterruptedException e) {
                    }
                }
            }

         */
        Timer timer = new Timer();
        for (int i = 0; i < 2; i++) {
            //2.创建一个定时任务对象
            LmyTimerTask lmyTimerTask = new LmyTimerTask("lmy" + i);
            //3.任务添加，将定时任务添加到定时器中。设置延迟时间为2s，即每2s执行一次该任务
            /*
                private void sched(TimerTask task, long time, long period) {
                    if (time < 0)
                        throw new IllegalArgumentException("Illegal execution time.");

                    // Constrain value of period sufficiently to prevent numeric
                    // overflow while still being effectively infinitely large.
                    if (Math.abs(period) > (Long.MAX_VALUE >> 1))
                        period >>= 1;

                    synchronized(queue) {
                        if (!thread.newTasksMayBeScheduled)
                            throw new IllegalStateException("Timer already cancelled.");

                        synchronized(task.lock) {
                            if (task.state != TimerTask.VIRGIN)
                                throw new IllegalStateException(
                                    "Task already scheduled or cancelled");
                            task.nextExecutionTime = time;
                            task.period = period;
                            task.state = TimerTask.SCHEDULED;
                        }

                        queue.add(task);    //将定时任务放入小顶堆中，放入小顶堆尾部，并上浮(堆化)
                        if (queue.getMin() == task)
                            queue.notify(); //唤醒队列，即解除阻塞状态
                    }
                }
             */
            //如第一次执行任务时间12:00:00  下一次任务执行时间理论上为第一次任务执行完毕后的时间+2s
            //即前一个任务执行完时间为12:00:03 那么下一此任务执行时间为12:00:05
            //此时就会存在任务延迟执行(丢任务)的情况
//            timer.schedule(lmyTimerTask, new Date(), 2000);

            //scheduleAtFixedRate 保证任务严格按照延迟时间来执行
            //但会存在任务执行时间混乱的问题
            timer.scheduleAtFixedRate(lmyTimerTask, new Date(), 2000);

        }
    }

}
